射频识别(RFID)技术

基于主机的卡模拟概览(翻译)

作者:陈广 日期:2019-11-20


很多提供 NFC 功能的 Android 设备都支持 NFC 卡模拟。多数情况下,卡通过设备中的单独芯片进行模拟,称为安全元件(secure element)。许多由无线运营商提供的 SIM 卡也包含一个安全元件。

Android 4.4 引入了一种附加的卡模拟方法,该方法不涉及安全元件,被称为基于主机的卡模拟。这使得任意 Android 应用程序都可以模拟卡并直接与 NFC 读写器对话。本文描述基于主机的卡模拟(HCE)是如何在 Android 上工作的,以及如何使用此技术开发一个 app 来模拟 NFC 卡。

使用安全元件模拟卡

当 NFC 卡模拟是由安全元件来提供时,要模拟的卡通过 Android 应用程序提供到设备上的安全元件中。那么,当用户将设备靠近 NFC 终端时,设备中的 NFC 控制器将来自读写器的所有数据直接路由到安全元件。图 1 演示了这个概念。

图 1:安全元件的 NFC 卡模拟

安全元件本身执行与 NFC 终端的通信,并且其事务根本不涉及 Andriod 应用。在事务完成后,Android 应用可以直接查询安全元件获取事务状态并通知用户。

基于主机的卡模拟

当 NFC 卡模拟使用的是基于主机的卡模拟时,数据直接路由至 Android 应用所在的主机 CPU 中,而不是将 NFC 协议帧路由至安全元件。图 2 演示了基于主机的卡模拟是如何工作的。

图 2:无安全元件的 NFC 卡模拟

支持的 NFC 卡和协议

图 3:Android 的 HCE 协议栈

NFC 标准提供了对很多不同协议的支持,可以模拟不同类型的卡。

Android 4.4 支持当今市场上常见的几种协议。许多现有的非接触式卡已经基于这些协议,例如非接触式支付卡。这些协议也得到了当今市场上许多 NFC 读写器的支持,包括 Android NFC 设备本身作为读写器的功能(参见IsoDep类)。这使得您可以仅使用 Android 设备在 HCE 周围构建和部署端到端 NFC 解决方案。

具体而言,Android4.4 支持基于 NFC 论坛 ISO-DEP 规范(基于 ISO/IEC 14443-4)和处理应用程序协议数据单元(APDU)的仿真卡,如 ISO/IEC 7816-4 规范所定义的那样。Android 任务仅基于 Nfc-A(ISO/IEC 14443-3 Type A)技术模拟 ISO-DEP。对 Nfc-B((ISO/IEC 14443-4 Type B)技术的支持是可选的。所有这些规范分层显示在图 3 中。

HCE 服务

Android 的 HCE 体系结构基于 Android 服务组件(称为"HCE 服务")。服务的主要优点之一是它可以在后台运行,而不需要任何用户界面。这天然适合许多 HCE 应用,如积份卡或交通卡,用户不应该启动应用程序使用它。相反,让 NFC 读写器轻敲设备会启动正确的服务(如果尚未运行),并在后台执行该事务。当然,您可以自由地从您的服务中启动额外的 UI(例如用户通知),如果这样做有意义的话。

服务选择

当用户将设备接近 NFC 读写器时,Android 系统需要知道 NFC 读写器实际上想要与哪个 HCE 服务对话。这就是 ISO/IEC 7816-4 规范的来源:它定义了一种以应用程序 ID(AID)为中心的选择应用程序的方法。AID 由 16 个字节组成。如果您正在为现有的 NFC 读写器基础设施模拟卡片,这些读者正在寻找的 AID 通常是众所周知的和公开注册的(例如,支付网络的 AID,如 Visa 和 MasterCard)。

如果您想为自己的应用程序部署新的读取器基础设施,则需要注册您自己的 AID。AID 的注册过程在 ISO/IEC 7816-5 规范中定义。谷歌建议,如果您正在为 Android 部署 HCE 应用程序,可以按照 7816-5 注册一个 AID,它可避免与其他应用程序发生冲突。

AID 组

在某些情况下,HCE 服务可能需要注册多个 AID 才能实现某个应用程序,并且需要确保它是所有这些 AID 的默认处理程序(与另一服务的组中的某些 AID 相反)。

AID 组是一系列被视为属于共同的操作系统 AID 。对于一个 AID 组中的 AID,Android 可以保证以下一项:

  • 组中的所有 AID 都被路由至此 HCE 服务。
  • 组中没有任何 AID 被路由至此 HCE 服务(例如,服务请求组中的一个或多个 AID,而用户优先选择另一服务)。

换句话说,不存在中间状态,其中该组中的一些 AID 可以被路由到一个 HCE 服务,而另一些可以路由到另一个 HCE 服务。

AID 组和类别

每个 AID 组都与一个类别关联。这使得 Android 可以按类别将 HCE 服务分组,并且反过来允许用户在类别级别上设置默认值而不是 AID 级别。通常,避免在应用程序中任何面向用户的部分中提到 AID:它们对普通用户没有任何意义。

Android 4.4 支持两种类别:CATEGORY_PAYMENT(覆盖行业标准支付应用)和CATEGORY_OTHER(对应于所有其它 HCE 应用)。

注意:在任何给定时间,在系统中只能启用CATEGORY_PAYMENT类别中的一个 AID 组。通常,这将是一个应用程序,了解主要的信用卡支付协议,可以在任何商家工作。

对于仅在一个商家(例如储值卡)工作的闭环支付应用,您应该使用CATEGORY_OTHER。该类别中的 AID 组可以总是活动的,并且在必要时可以在 AID 选择期间由 NFC 读写器给予优先级。

实现一个 HCE 服务

要基于主机卡模拟来模拟一张 NFC 卡,您需要创建一个处理 NFC 事务的服务组件。

检查 HCE 支持

应用程序可通过检查FEATURE_NFC_HOST_CARD_EMULATION功能来确定设备是否支持 HCE。您可以在应用程序的 manifest 中使用<uses-feature>标记来声明程序使用 HCE 功能,以及它对于 app 的功能是否是必须的。

服务实现

Android 4.4 提供了一个方便的服务类,可以作为实现 HCE 服务的基础:HostApduService类。

因此,第一步是扩展HostApduService

public class MyHostApduService extends HostApduService {
    @Override
    public byte[] processCommandApdu(byte[] apdu, Bundle extras) {
       ...
    }
    @Override
    public void onDeactivated(int reason) {
       ...
    }
}

HostApduService声明了两个抽象方法,需要被重写和实现。

每当 NFC 读写器向您的服务发送应用程序协议数据单元(APDU)时,都会调用processCommandApdu()。APDU 在 ISO/IEC 7816-4 规范中声明。APDU 是 NFC 读写器与您的 HCE 服务之间交换的应用程序级别的数据包。应用程序级的协议是半双工的:NFC 读写器将向您发送 APDU 命令,并等待您在返回中发送响应 APDU。

注意:ISO/IEC 7816-4 规范还定义了多逻辑信道的概念,其中可以在单独的逻辑信道上具有多个并行的 APDU 交换。然而,Android 的 HCE 实现仅支持单个逻辑信道,因此仅有单个线程交换 APDU。

如前所述,Android 使用该 AID 来确定读写器想要与哪些 HCE 服务交谈。通常,NFC 读写器发送到设备的第一个 APDU 是“SELECT AID”;此 APDU 包含读写器想要通信的 AID。Android 从 APDU 中提取 AID,将其解析为 HCE 服务,然后将该 APDU 转发到已解析的服务。

您可以通过从processCommandApdu()返回的响应 APDU 的字节来发送响应 APDU。请注意,此方法在应用程序的主线程上调用,不能被阻塞。因此,如果不能立即计算和返回响应 APDU,则返回 null。然后,您可以在另一个线程执行必要的工作,并使用HostApduService类中定义的sendResponseApdu()方法发送响应。

Android 会持续将新的 APDU 从读写器转发到您的服务,直到:

  1. NFC 读写器发送另一个“SELECT AID” APDU,OS 解析为不同的服务;
  2. NFC 读写器与设备之间的 NFC 链接已断开。

在这两种情况下,类的onDeactivated()实现都会被调用,其携带一个参数,用于说明这两个事件中的哪一个发生了。

如果您正在使用现有的读写器基础设施,则需要实现读写器在 HCE 服务中所期望的现有应用程序级别协议。

如果您正在部署新读写器基础设施,则可以定义您自己的协议和 APDU 序列。通常,请尝试限制 APDU 的数量和需要交换的数据的大小:这将确保用户的设备只需在 NFC 读写器上保持很短持一段时间。合理的上限约为 1KB 的数据,通常可以在 300ms 内进行交换。

manifest 中声明服务和注册 AID

您的服务必须像往常一样在 manifest 中声明,但是还必须向服务声明中添加一些额外的部分。

首先,要告诉平台它是实现HostApduService接口的 HCE 服务,您的服务声明必须包含SERVICE_INTERFACE action 的 intent filter。

此外,要告诉平台该服务请求哪些 AID 组,服务声明中必须包含一个SERVICE_META_DATA<meta-data>标记,该标记指向一个 XML 资源,其中包含有关 HCE 服务的其他信息。

最后,您必须将android:exported属性设置为true,并在服务声明中要求android.permission.BIND_NFC_SERVICE权限。前者确保服务可以被外部应用程序绑定。后者强制执行只有拥有android.permission.BIND_NFC_SERVICE权限的外部应用程序才能绑定到您的服务。因为android.permission.BIND_NFC_SERVICE是一个系统权限,这实际上强制规定只有 Android 操作系统才能绑定到您的服务。

以下是一个HostApduService manifest 声明示例:

<service android:name=".MyHostApduService" android:exported="true"
         android:permission="android.permission.BIND_NFC_SERVICE">
    <intent-filter>
        <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/>
    </intent-filter>
    <meta-data android:name="android.nfc.cardemulation.host_apdu_service"
               android:resource="@xml/apduservice"/>
</service>

这个meta-data标签指向 apduservice.xml 文件。包含两个专有 AID 的单个 AID 组声明的这样一个文件的示例如下所示:

<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
           android:description="@string/servicedesc"
           android:requireDeviceUnlock="false">
    <aid-group android:description="@string/aiddescription"
               android:category="other">
        <aid-filter android:name="F0010203040506"/>
        <aid-filter android:name="F0394148148100"/>
    </aid-group>
</host-apdu-service>

<host-apdu-service>标签需要包含一个<android:description>属性,用于描述服务,可显示在 UI。requireDeviceUnlock属性可用于指定必须解锁设备才能调用此服务来处理 APDU。

<host-apdu-service>必须包含一个或多个<aid-group>标签。每个<aid-group>标签都需要:

  • 包含一个android:description属性,用于容纳有关 AID 组的适合在 UI 显示的描述。
  • 将它的android:category属性设置为指示 AID 组所属的类别,例如,CATEGORY_PAYMENTCATEGORY_OTHER所定义的字符串常量。
  • 每个<aid-group>必须包含一个或多个<aid-filter>标签,标签中包含一个 AID。AID 必须使用十六进制格式,且为偶数个字符。

最后,您的应用程序还需要持有 NFC 权限才能注册为 HCE 服务。

AID 冲突解决

可以在单个设备上安装多个HostApduService组件,并且可以通过多个服务注册相同的 AID。根据 AID 所属的类别,Android 平台解决了 AID 冲突。每个类别可具有不同的冲突解决策略。

例如,对于某些类别(如支付),用户可以在 Android UI 设置中选择默认服务。对于其他类别,策略可能总是询问用户在发生冲突时将调用哪个服务。若要查询某个类别的冲突解决策略,请参考getSelectionModeForCategory()

检查您的服务是否是默认的

应用程序可通过使用isDefaultServiceForCategory(ComponentName, String) API 检查它的 HCE 服务对于特定类别是否是默认服务。

如果您的服务不是默认的,则可以请求将其设置为默认值。参考ACTION_CHANGE_DEFAULT

支付应用

Android 认为 HCE 服务已经宣布了一个以“支付”类别作为支付应用程序的 AID。Android 4.4 版本包含一个顶级设置菜单项,名为“tap & pay”,其枚举所有这样的支付应用。在此设置菜单中,当一个支付终端触发时,用户可以选择将要调用手默认支付应用。

支付应用所需的内容

为了提供更具视觉吸引力的用户体验,HCE 支付应用程序需要为其服务提供额外的内容:所谓的服务标语。此内容大小为 260×96 dp,可在 meta-data XML 文件中指定,具体操作为在<host-apdu-service>标签中添加android:apduServiceBanner属性,其指向 drawable 资源。下面显示一个示例:

<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
        android:description="@string/servicedesc"
        android:requireDeviceUnlock="false"
        android:apduServiceBanner="@drawable/my_banner">
    <aid-group android:description="@string/aiddescription"
               android:category="payment">
        <aid-filter android:name="F0010203040506"/>
        <aid-filter android:name="F0394148148100"/>
    </aid-group>
</host-apdu-service>

屏幕关闭和锁定屏幕行为

当设备的屏幕关闭时,当前的 Android 实现会将 NFC 控制器和应用程序处理器完全关闭。因此,当屏幕关闭时,HCE 服务将无法工作。

然而,HCE 服务可以在锁定屏幕时运行:这是由 HCE 服务的<host-apdu-service>标签中的android:requireDeviceUnlock属性控制的。默认情况下,不需要设备解锁,即使设备被锁定,您的服务也会被调用。

如果您将 HCE 服务的android:requireDeviceUnlock属性设置为true,则 Android 将提示用户在轻敲 NFC 读写器时解锁该设备,该 NFC 读写器选择了已解析为您的服务的 AID。解锁后,Android 将显示一个对话框,提示用户再次点击以完成事务。这是必要的,因为用户可能已经将设备从 NFC 读写器移开,以便将其解锁。

与安全元件卡共存

本节部署了一个开发人员很感兴趣的依赖于卡模拟的安全元件的应用程序。Android 的 HCE 实现旨在与其他实现卡模拟的方法并行工作,包括使用安全元件。

注意:Android 未提供用于与安全元件本身直接通信的 API。

这种共存基于一个被称为"AID 路由"的原理:NFC 控制器保持路由表,该路由表由(有限)路由规则列表组成。每个路由规则都包含一个 AID 和一个目的地。目的地即可以是主机 CPU(运行 Android 应用程序的地方),也可以是连接的安全元件。

当 NFC 读写器发送一个携带“SELECT AID”的 APDU,NFC 控制器解析它并检查 ADI 是否匹配路由表中的某个 AID。如果匹配,那么 APDU 和它后面的所有 APDU 将被发送到与 AID 相关的目的地,直到收到另一个“SELECT AID” APDU 或 NFC 链接被破坏为止。

注意:当 ISO/IEC 7816-4 还定义“部分匹配”的概念,现在它还未被 Android HCE 设备支持。

这个体系结构如图 4 所示。

图 4:使用安全元件和主机卡模拟操作的 Android

NFC 控制器通常还包含一个用于 APDU 的默认路由。当路由表中无法找到 AID 时,则使用默认路由。虽然这个设置可能因设备不同而有所不同,但 Android 设备需要确保应用程序注册的 AID 被正确地路由到主机。

实现了 HCE 服务或使用安全元件的 Android 应用程序不必担心配置路由表 —— 这由 Android 自动配置。Android 只需要知道哪些 AID 可以由 HCE 服务处理,哪些 AID 可以由安全元件处理。根据安装的服务和用户配置为首选的服务,路由表将自动配置。

我们已经描述了如何声明 HCE 服务的 ADI。下一节解释如何为使用安全元件进行卡片模拟的应用程序声明 AID。

安全元件 AID 注册

使用安全元素进行卡片模拟的应用程序可以在其 manifest 中声明一个所谓的“关闭主机服务”。此类服务的声明几乎与 HCE 服务的声明相同。例外情况是:

  • intent filter 中使用的 action 必须设置为SERVICE_INTERFACE
  • meta-data 的name属性必须设置为SERVICE_META_DATA
  • meta-data 的 XML 文件必须使用<offhost-apdu-service>根标签。
<service android:name=".MyOffHostApduService" android:exported="true"
         android:permission="android.permission.BIND_NFC_SERVICE">
    <intent-filter>
        <action android:name="android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE"/>
    </intent-filter>
    <meta-data android:name="android.nfc.cardemulation.off_host_apdu_service"
               android:resource="@xml/apduservice"/>
</service>

示例对应的 apduservice.xml 文件注册了两个 AID:

<offhost-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
           android:description="@string/servicedesc">
    <aid-group android:description="@string/subscription" android:category="other">
        <aid-filter android:name="F0010203040506"/>
        <aid-filter android:name="F0394148148100"/>
    </aid-group>
</offhost-apdu-service>

android:requireDeviceUnlock属性不适用于 off host 服务,因为主机 CPU 不涉及事务,因此不能阻止安全元件在设备锁定时执行事务。

android:apduServiceBanner属性必须用于支付应用程序的 off host 服务,以便作为默认支付应用程序选择。

Off host 服务调用

Android 本身不会启动或绑定到声明为“off host”的服务。这是因为实际事务是由安全元件执行,而不是由 Android 服务本身执行的。服务声明只允许应用程序注册安全元件上的 AID。

HCE 和安全

HCE 体系结构本身提供了一个核心的安全性:因为您的服务受到BIND_NFC_SERVICE系统权限的保护,所以只有 OS 才能与您的服务绑定和通信。这确保您所接收的任何 APDU 实际上是由来自 NFC 控制器的操作系统接收的 APDU,并且您发送回的任何 APDU 只能到OS,而 OS 又将 APDU 直接转发到 NFC 控制器。

剩下的核心部分是去哪获取应用程序发送给 NFC 读写器的数据。在 HCE 设计中,这是有意解耦的。它不关心数据来自何处,它只是确保数据安全地传输到 NFC 控制器和 NFC 读写器。

为了安全地存储和检索您要从 HCE 服务发送的数据,您可以依靠 Android 应用程序沙盒,它将应用程序的数据与其他应用程序隔离开来。有关 Android 安全的更多详细信息,请阅读安全提示

协议参数和详细信息

本节讲述开发人员感兴趣的,在 NFC 协议的防冲突和激活阶段使用 HCE 设备的哪些协议参数。这使得构建与 Android HCE 设备兼容的读写器基础设施成为可能。

Nfc-A (ISO/IEC 14443 type A) 协议的防冲突和激活

作为 Nfc-A 协议激活操作的一部分,多帧交换。

在交换的第一部分,HCE 设备将呈现其 UID;应假定 HCE 设备具有随机的 UID。这意味着在每次触碰时,呈现给读者的 UID 将是一个随机生成的 UID。因此,NFC 读写器不应依赖于 HCE 设备的 UID 作为证实或标识的形式。

NFC 读写器随后可通过发送SEL_REQ命令来选择 HCE 设备。HCE 设备的SEL_RES响应至少将第6位置 1(0x20),指示该设备支持 ISO-DEP。注意,SEL_RES 中的其他位也可以设置,例如表示对 NFC-DEP(p2p)协议的支持。由于可以设置其他位,希望与 HCE 设备交互的读写器应该只显式地检查第6位,而不是比较整个 SEL_RES 的值为0x20。

ISO-DEP 激活

在 Nfc-A 协议被激活之后,由 NFC 读写器发起 ISO-DEP 协议激活。它发送“RATS”(Request for Answer To Select)命令。RATS 响应,即ATS,完全由 NFC 控制器生成,而不是由 HCE 服务配置的。然而,需要 HCE 实现以满足 NFC 论坛对 ATS 响应的要求,因此 NFC 读写器可以依赖这些参数满足 NFC 论坛对任意 HCE 设备的要求。

以下部分对于由 HCE 设备上的 NFC 控制器发出的 ATS 响应,按字节提供更详细的描述:

  • TL:ATS 响应的长度。指示的长度不能超过 20 bytes。
  • T0:所有的 HCE 设备必须将位 5、6、7 置 1,标志着 TA(1)、TB(1)和TC(1)被包含在 ATS 响应中。位 1 至 4 指示 FSCI,编码最大帧尺寸。在 HCE 设备中,FSCI 的值必须在 0h 至 8h 之间。
  • T(A)1:定义读写器和模拟器之间的比特率,以及它们是否是不对称的。对于 HCE 设备没有比特率的要求或保证。
  • T(B)1:位 1 到 4 指示启动帧保护时间整数(Start-up Frame Guard time Integer,SFGI)。在 HCE 设备中,SFGI 必须小于 8h。位 5 至 8 指示帧等待时间整数(Frame Waiting time Integer,FWI)以及侦等待时间(Frame Waiting Time,FWT)。在 HCE 设备中,FWI 必须小于 8h。
  • T(C)1:位 5 指示对“高级协议功能”的支持,即 HCE 设备是否支持“高级协议功能”。位 2 指示对 DID 的支持,即 HCE 设备是否支持 DID。位 1 指示对 NAD 的支持,HCE 设备不可以支持 NAD,也就是将位 1 设置为 0。
  • Historical 字节:HCE 设备可以返回多达 15 个 historical 字节。愿意与 HCE 服务交互的 NFC 读写器不应假设 historical 字节的内容或它们的存在。

请注意,许多 HCE 设备很可能符合 EMVCo 中统一支付网络在其“非接触式通信协议”规范中指定的协议要求。特别是:

  • T0 中的 FSCI 必须在 2h 到 8h 之间。
  • T(A)1 必须设置为 0x80,指示仅支持 106kbit/s 比特率,并且不支持读写器和模拟器之间的不对称比特率。
  • T(B)1 中的 FWI 必须小于 7h。

APDU 数据交换

如前所述,HCE 实现只支持单个逻辑信道。试图在不同的逻辑信道上选择应用程序将无法在 HCE 设备上工作。

;

© 2018 - IOT小分队文章发布系统 v0.3